package rwt.server.mtl.java;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Map;

import org.slf4j.*;

import rwt.server.core.BaseMessage;
import rwt.server.mtl.*;

/**
 * Converts RailMessages to and from Json format and byte array format.
 */
public final class JavaMessageAdaptor implements IMessageAdaptor
{
	private static final Logger logger = LoggerFactory.getLogger(JavaMessageAdaptor.class);

	private Map<Integer, Class<? extends BaseMessage>> idToClassMap;
	private MethodDictionary methods = new MethodDictionary();
	private JsonConverter jsonConverter;
	
	/**
	 * Constructs a new message adaptor.
	 */
	public JavaMessageAdaptor(Map<Integer, Class<? extends BaseMessage>> idToClassMap)
	{
		this.idToClassMap = idToClassMap;
		this.jsonConverter = new JsonConverter(idToClassMap);
	}
	
	public byte[] messageToBytes(BaseMessage message) throws IOException
	{
		// Calls an MTL Make method
		
		if (message.id == BaseMessage.INVALID_ID)
			throw new IOException("Message ID is invalid");
		
		Class<? extends BaseMessage> messageClass = message.getClass();
		try
		{
			Method makeMethod = methods.getMakeMethod(messageClass.getSimpleName());
			byte[] bytes = (byte[]) makeMethod.invoke(null, message);
			return bytes;
		}
		catch (Exception e)
		{
			logger.warn("", e);
		}
		
		return null;
	}
	
	public BaseMessage bytesToMessage(byte[] bytes)
	{
		// Calls an MTL Split method
		
		int opCode = (int) bytes[0] & 0xff;
		if (opCode == 0)
		{
			// Extended message
			opCode = (int) bytes[1] & 0xff;
		}
		
		Class<? extends BaseMessage> messageClass = idToClassMap.get(opCode);
		if (messageClass == null)
		{
			logger.warn("Railroad message with op-code '" + opCode + " (0x"
					+ Integer.toHexString(opCode) + ")' " + "is not bound to a message class");
			return null;
		}
		
		try
		{
			Method splitMethod = methods.getSplitMethod(messageClass.getSimpleName());
			
			if (splitMethod == null)
			{
				logger.warn("Java MessageTranslationLibrary has no Split procedure for \"" + 
						messageClass.getSimpleName() + "\". Ignoring message.");
				return null;
			}
			
			BaseMessage message = (BaseMessage) splitMethod.invoke(null, bytes);
			
			return message;
		}
		catch (Exception e)
		{
			logger.warn("", e);
		}
		
		return null;
	}
	
	public BaseMessage jsonToMessage(String json) throws IOException
	{
		return jsonConverter.fromJson(json);
	}

	public String messageToJson(BaseMessage message) throws IOException
	{
		return jsonConverter.toJson(message);
	}
}
